package demo.数据类型;

/**
 * 基本数据类型demo
 * ● 封装类是引用类型，基本类型在传递参数的时候都是按值传递，而封装类型是按引用传递的（其实引用也是按值传递的，但是传递的是对象的地址）
 *
 * ● 它们的默认值不同基础数据类型是0和封装类型是null，基本数据类型都是final修饰的，不能继承扩展新的类和方法
 *
 * ● 基本类型在内存中存储在栈中，引用类型的引用（值的地址）存储在栈中，而实际的对象（值）是存在堆中
 *
 * ● 基本数据类型的好处就是速度快不涉及到对象的构造和回收，封装类的目的是为了更好地处理数据之间的转换
 */
public class Data {

    public static void main(String[] args)
    {
        /*
            1. 运算符号 += 和 =，+ 的区别
         */
        //java中+=的意义包含两部分，一是”+”，就是通常所说的直接相加，二是改变结果的类型，将计算结果的类型转换为”+=符号左边的类型。
        int a = 1; a+=2;
        int b = 1; b=b+2;

        //对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型，因此 s1+1 运算结果也是 int 型，需要强制转换类型才能赋值给 short 型。
        //
        //而 short s1 = 1; s1 += 1;可以正确编译，因为 s1+= 1; 相当于 s1 = (short)(s1 + 1); 其中有隐含的强制类型转换。
//        System.out.printf("a:%s\n", a);
//        System.out.printf("b:%s\n", b);


        //2.int和Integer有什么区别？[自动装箱/拆箱 机制]
        //基本数据类型的默认值与基本数据类型的包装类的默认值不同。包装类因为是类，所以包装类的默认值为null；
        // 基本数据类型，byte、short、int、long为0，boolean默认为false，float和double为0.0，char类型默认为空(不是null哦)
//        main2();

        //3.Integer缓存机制、装箱拆箱实现原理
        // 即Integer可以区分出未赋值和值为0的区别，int则无法表达出未赋值的情况，例如，要想表达出没有参加考试和考试成绩为0的区别，则只能使用Integer。
//        main3();

        //4.数据类型之间转换
        /**
         *         隐式转换：即自动转换，它不要求加以声明，是系统默认的。它是由小至大的转换：
         *         转换规则从存储范围小的类型到存储范围大的类型(只有前面的数据才能随便转换为后边的)
         *          byte—> short,char—> int —> long—> float —> double
         *
         *         显式转换：即强制转换，它是由大至小的转换，它的格式为：
         *         类型 变量=（类型） 变量2；
         */
//        main4();
        //5.定义变量 float f = 5.2;是否有问题？能否通过编译？
        /**
         *   有问题，不能通过编译；在Java中没小数点的默认是int，有小数点的默认是double。将double转换为float需要进行强制类型转换，如 float f = (float)5.2;或者float f = 5.2f;
         *   表示这是一个float类型的数据。
         */

        //6.三目运算符的类型对齐和拆箱
        main6();



    }


    /**
     * 2.int和Integer有什么区别？[自动装箱/拆箱 机制]
     *         ● java 是一个完全面向对象编程语言，但是为了编程的方便还是引入了基本数据类型，为了能够将这些基本数据类型当成对象操作，Java 为每一个基本数据类型都引入了对应的包装类型（wrapper class），int 的包装类就是Integer，从 Java 5 开始引入了 自动装箱/拆箱 机制，使得二者可以相互转换。
     *
     *         ● java 为每个原始类型提供了包装类型：（再说一遍！！！）
     *
     *         ● 基本数据类型: boolean，char，byte，short，int，long，float，double
     *
     *         ● 包装类型：Boolean，Character，Byte，Short，Integer，Long，Float，Double
     */
    public static void main2()
    {
        /**
         * 500是基本数据类型int
         * x是Integer包装类型
         * 基本数据类型  --->  包装类型（自动转换，自动装箱机制）
         */
        Integer x=500;
        System.out.println(x);

        /**
         * x是Integer包装类型
         * y是基本数据类型int
         * 包装类型  --->  基本数据类型（自动转换，自动拆箱机制）
         */
        int y=x;
        System.out.println(y);

        /**
         * 这里程序正常执行不报错，是因为自动拆箱机制
         * + 号两边要求是基本数据类型，z是包装类，所以这里为进行自动拆箱机制进行自动转换
         */
        Integer z=1000;
        System.out.println(z+1);
    }

    /**
     * 3.Integer缓存机制、装箱拆箱实现原理
     */
    public static void main3() {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
//        System.out.println(f1 == f2);
//        System.out.println(f3 == f4);
        /*
        答案：第一行输出 true，第二行输出 false。
        首先需要注意的是 f1、f2、f3、f4 四个变量都是 Integer 对象引用，所以下面的==运算比较的不是值而是引用。这里就要提到自动装箱机制了，那么自动装箱的本质是什么呢？
        当我们给一个Integer 对象赋一个 int 值的时候，会调用 Integer 类的静态方法 valueOf，如果看看valueOf的源代码就知道发生了什么。

        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
        IntegerCache 是 Integer 的内部类，其代码如下所示：
        private static class IntegerCache {.........}
        这一大堆源码简单地说就是：如果整型字面量的值在-128 到 127 之间，那么不会 new 新的 Integer 对象，而是直接引用常量池中的Integer对象，
        所以上面的面试题中f1==f2的结果是 true，而f3==f4 的结果是false。
         */


        /**
         * a与b、a与c使用= =比较，会将Integer包装类自动拆箱为基本数据类型中的int,进行值比较与a与b、a与c、b与c使用equals（包装类重写了equals方法）方法一样进行的都是值比较，所以是true；
         * 而b与c使用==进行比较的结果却为false，这是因为new出来的对象不会使用高频缓存范围的数值，是先创建对象，这两个对象是不同的对象，所以地址是不同的，返回false；
         * ————————————————
         * Java中 int、Integer和 new Integer() 使用==比较
         * int则是java的一种基本数据类型，其定义的是基本数据类型变量 ；Integer是int的包装类，其定义的是引用类型变量
         * 基本数据类类型存的是数值本身；引用类型变量在内存放的是数据的引用（）
         * 基本类型比较的是他们的值大小(通过)，而引用类型比较的是他们的引用地址==
         * Integer变量必须实例化后才能使用，而int变量不需要
         * Integer实际是对象的引用，当new一个Integer时，实际上是生成一个指针指向此对象；而int则是直接存储数据值
         * Integer的默认值是null，int的默认值是0
         * ————————————————
         *
         * 两个通过new生成的Integer变量永远是不相等的。
         * 因为new生成的是两个对象，其内存地址不同。
         *
         * Integer变量和int变量比较时，只要两个变量的值是向等的，则结果为true。
         * 因为包装类Integer和基本数据类型int比较时，java会自动将Integer拆箱为int，然后进行比较，实际上就变为两个int变量的比较。
         *
         * 非new生成的Integer变量和new Integer()生成的变量比较时，结果为false。
         * 因为非new生成的Integer变量指向的是java常量池中的对象，而new Integer()生成的变量指向堆中新建的对象，两者在内存中的地址不同。
         *
         * 两个非new生成的Integer对象，进行比较时，如果两个变量的值在区间-128到127之间，则比较结果为true，如果两个变量的值不在此区间，则比较结果为false
         * 因为-128到127java已经进行了缓存。
         * ————————————————
         * equals与==的区别：
         *  ==是判断两个变量或实例是不是指向同一个内存空间，equals是判断两个变量或实例所指向的内存空间的值是不是相同
         * ==是指对内存地址进行比较 ， equals()是对字符串的内容进行比较
         * ==指引用是否相同， equals()指的是值是否相同
         */
        int a = 100;
        Integer b = new Integer(100);
        Integer c = new Integer(100);
        Integer d = 150;
        Integer e = 150;
        System.out.println(a == b);
        System.out.println(a == c );
        System.out.println(b == c);
        System.out.println(b.equals(a));
        System.out.println(c.equals(a));
        System.out.println(b.equals(c));
        /**
         * 同类型的进行比较，如Integer 与int，Long与long进行==比较时，会自动拆箱；
         * 不同类型之间进行比较，如果有一方为非包装类，则会自动拆箱。
         * 如果两方都为包装类(非new 情况)，则不会拆箱，且不能比较，编译会报错
         */
        System.out.println(d == e);//


    }

    /**
     * 4.数据类型之间转换
     * ● 字符串如何转基本数据类型？
     *
     * 调用基本数据类型对应的包装类中的方法 parseXXX(String)或valueOf(String)即可返回相应基本类型。
     *
     * ● 基本数据类型如何转字符串？
     *
     * 一种方法是将基本数据类型与空字符串（""）连接（+）即可获得其所对应的字符串；另一种方法是调用 String类中的 valueOf()方法返回相应字符串。
     * ————————————————
     */
    public static void main4 () {
        // String ---> int
        String s1="100";
        int i1=Integer.parseInt(s1);
        System.out.println("String ---> int " + i1);

        // int ---> String
        String s2=i1 + "";
        System.out.println("int ---> String " + s2 + 1);

        // int ---> Integer (自动装箱)
        Integer x=1000;
        System.out.println("x = " + x);

        // Integer ---> int (自动拆箱)
        int y=x;
        System.out.println("y = " + y);

        // String ---> Integer
        Integer k=Integer.valueOf("123");
        System.out.println("String ---> Integer " + k);

        // Integer ---> String
        String str=String.valueOf(k);
        System.out.println("Integer ---> String " + str);
    }

    public static void main5 () {

    }

    /**
     * 6.三目运算符的类型对齐和拆箱
     *
     */
    public static void main6 () {
        /**
         * 在以下场景会触发类型对其的拆箱操作：
         * 1.表达式1或者表达式2其中一个是原始值
         * 2.表达式1和表达式2的值类型不一致，会自动拆箱升级为范围类型更大的那个类型
         * byte——>short——>int——>long——>float——>double
         * char——>int——>long——>float——>double
         * */
        //第一个输出语句的第一个表达式是1.0，第二个表达式是99。这时Java会根据运算符的精度进行自动类型转换。由于1.0的原因，99也会自动变成99.0。因此此题的真正输出为 99.0 。
        System.out.println(false?1.0:99);//99.0
        char a = 'b';
        int b = 65;
        boolean flg = true;
        //在使用三目运算符处理包装类时，要注意 NPE 问题
        System.out.println(flg ? a : b);  // 65
        System.out.println(flg ? b : a);       // 65


        char x='x';
        // 至于第三个输出，Java编程规范中提到：当后两个表达式有一个是常量表达式时，另外一个类型是T时，而常量表达式可以被T表示时,输出结果是T类型。所以，因为97是常量，可以被char表示。输出结果是char型，所以输出为a。
        System.out.println(false ? x : 97); // a
        //char的范围，分析如下代码 [char的范围是 0-65535 ，所以当 (flg ? x : 65535) 时，会返回x]
        System.out.println(flg ? x : 65535);//x
        /**
         * 反例：将代码变成下面这样，按理说应该不会有什么问题，因为 c 是包装类型，可以接收 null ，但是运行时还是会发生 NullPointerException 异常.
         * 其实，当三目运算符的第二、第三个操作数分别为基本类型的包装类和基本数据类型时，并且选择了包装类那一项，则包装类会自动进行拆箱操作。
         * 即代码还是会进行拆箱操作，所以会发生NullPointerException 异常，这点非常容易使人弄错。
         */
        int aa = 5;
        Integer bb = null;

        Integer c = true ? bb : aa;
        System.out.println(c);

    }


}
